// $Id: model.cpp 5 2009-09-05 10:21:39Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <cstdio> // sprintf
#include <cstdlib>
#include <cstring> // memset
#include <stdexcept>
#include <sstream>
#include <new>
#include <boost/assert.hpp>
#include <boost/foreach.hpp>
#include <xpgen/model.hpp>
#include <xpgen/ns.hpp>

using namespace std;

namespace xpgen {

// uuid
string uuid::to_str() const
{
    // XXXXXXXX-XXXX-xxxx-XXXX-XXXXXXXXXXXX
    char buf[256];
    int n = sprintf(
            buf, "%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X",
            (unsigned)m0, (unsigned)m1, (unsigned)m2,
            (unsigned)m3[0], (unsigned)m3[1],
            (unsigned)m3[2], (unsigned)m3[3],
            (unsigned)m3[4], (unsigned)m3[5],
            (unsigned)m3[6], (unsigned)m3[7]);

    return string(buf, n);
}

string uuid::to_value_str() const
{
    // { 0xXXXXXXXX, 0xXXXX, 0xxxxx, { 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX } }
    char buf[256];
    int n = sprintf(
            buf, "{ 0x%08X, 0x%04X, 0x%04x, { 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X } }",
            (unsigned)m0, (unsigned)m1, (unsigned)m2,
            (unsigned)m3[0], (unsigned)m3[1],
            (unsigned)m3[2], (unsigned)m3[3],
            (unsigned)m3[4], (unsigned)m3[5],
            (unsigned)m3[6], (unsigned)m3[7]);

    return string(buf, n);
}

// type_decl
type_decl::type_decl() :
    tag(0),
    is_array(false),
    itf(0)
{
}

type_decl::~type_decl()
{
}

// param_decl
param_decl::param_decl(method_decl* method_) :
    method(method_),
    flags(0),
    length_is(0),
    qi_index(0),
    primary_count(0),
    user_data(0)
{
    // To make it clear
    primary_ = 0;
}

param_decl::~param_decl()
{
    if (primary_count > 1)
        free(primaries_);
}

string param_decl::context_name() const
{
    string ret;

    BOOST_ASSERT(method);
    ret = method->context_name();
    ret += ": ";
    ret += name;

    return ret;
}

param_decl* param_decl::primary() const
{
    if (primary_count == 0) {
        BOOST_ASSERT(!primary_);
        return 0;
    } else if (primary_count == 1) {
        BOOST_ASSERT(primary_);
        return primary_;
    } else {
        BOOST_ASSERT(primaries_);
        BOOST_ASSERT(primaries_[0]);
        return primaries_[0];
    }
}

param_decl* param_decl::primary(unsigned n) const
{
    BOOST_ASSERT(n < primary_count);
    return n == 0 ? primary() : primaries_[n];
}

void param_decl::add_primary(param_decl* primary)
{
    BOOST_ASSERT(primary);

    if (is_primary()) {
        ostringstream s;
        s << "trying to add primary param to primary param in ("
          << context_name() << ")";
        throw runtime_error(s.str());
    }

    if (primary_count == 0) {
        primary_ = primary;
        primary_count = 1;
    } else if (primary_count == 1) {
        BOOST_ASSERT(primary_);
        param_decl* tmp = primary_;
        param_decl** p = (param_decl**)malloc(2 * sizeof(param_decl*));
        if (!p)
            throw bad_alloc();
        primaries_ = p;
        primaries_[0] = tmp;
        primaries_[1] = primary;
        primary_count = 2;
    } else {
        BOOST_ASSERT(primaries_);
        param_decl** p = (param_decl**)realloc(
                primaries_, (primary_count + 1) * sizeof(param_decl*));
        if (!p)
            throw bad_alloc();
        primaries_ = p;
        primaries_[primary_count++] = primary;
    }
}

// constant_decl
constant_decl::constant_decl(interface_decl* itf_) :
    itf(itf_),
    user_data(0)
{
    memset(&value, 0, sizeof(value));
}

constant_decl::~constant_decl()
{
}

string constant_decl::context_name() const
{
    string ret;

    BOOST_ASSERT(itf);
    ret = itf->context_name();
    ret += ".";
    ret += name;

    return ret;
}

// method_decl
method_decl::method_decl(interface_decl* itf_) :
    itf(itf_),
    result(0),
    flags(0),
    qi_count(0),
    user_data(0)
{
    result = new param_decl(this);
}

method_decl::~method_decl()
{
    delete result;
    BOOST_FOREACH(param_decl* p, params)
    {
        delete p;
    }
}

string method_decl::context_name() const
{
    string ret;

    BOOST_ASSERT(itf);
    ret = itf->context_name();
    ret += ".";
    ret += name;

    return ret;
}

bool method_decl::is_scriptable() const
{
    return itf->is_scriptable() && !is_hidden() && !is_notxpcom();
}

// interface_decl
interface_decl::interface_decl(module_decl* module_) :
    module(module_),
    base(0),
    flags(0),
    user_data(0)
{
}

interface_decl::~interface_decl()
{
    BOOST_FOREACH(constant_decl* p, constants)
    {
        delete p;
    }
    BOOST_FOREACH(method_decl* p, methods)
    {
        delete p;
    }
}

string interface_decl::context_name() const
{
    return qname;
}

bool interface_decl::is_base_of(const interface_decl* rhs) const
{
    return rhs->base == this;
}

bool interface_decl::is_ancestor_of(const interface_decl* rhs) const
{
    if (this != rhs) {
        while (rhs->base) {
            if (rhs->base == this)
                return true;
            rhs = rhs->base;
        }
    }

    return false;
}

void interface_decl::set_base(interface_decl* base_)
{
    base = base_;
}

void interface_decl::set_name(const string& name_, const string& ns_)
{
    name = name_;
    ns = ns_;

    ns::get_ns_list(ns_list, ns);
    ns_cpp = ns::get_ns_cpp(ns_list);

    qname = ns::get_qname(ns, name);
    qname_cpp = ns::get_qname_cpp(ns_cpp, name);
}

void interface_decl::set_iid(const uuid& iid_)
{
    iid = iid_;
    iid_str = iid.to_str();
    iid_value_str = iid.to_value_str();
}

// module_decl
module_decl::module_decl() :
    user_data(0)
{
}

module_decl::~module_decl()
{
    BOOST_FOREACH(interface_decl* p, interfaces)
    {
        delete p;
    }
}

} // namespace xpgen
